import assert from 'assert';
import {
  trim, find, map, isEmpty
} from 'lodash';
import dayjs from 'dayjs';
import { Op } from 'sequelize';
import { validationResult } from 'express-validator';
import ExpressValidationError from '../utils/validation-error';

import BaseController from './base';
import UserModel from '../models/user';
import { getSequelize } from '../db';
import { signToken, generatePermissionTree } from '../utils';
import SysMenus from '../models/sys-menu';
import Hotels from '../models/hotel';
import UserHotels from '../models/user-hotel';
import UserPermissions from '../models/user-permission';
import AppParameterValues from '../models/app-parameter-values';

function generateVueRouters(vueRouters, permissions, routers) {
  for (const router of routers) {
    // console.log(router);
    if (router.permissions) {
      permissions.push(router.permissions);
    }

    if (router.menu_type !== 'F') {
      const item = {
        path: router.path,
        component: router.component,
        name: router.name,
        redirect: router.redirect,
        meta: {
          alwaysShow: router.alwaysShow,
          hidden: router.hidden,
          title: router.title,
          icon: router.icon,
          noCache: router.noCache,
          breadcrumb: router.breadcrumb,
          affix: router.affix,
          noTagsView: router.noTagsView,
          activeMenu: router.activeMenu,
          canTo: router.canTo
        }
      };

      vueRouters.push(item);
      if (Array.isArray(router.children) && router.children.length > 0) {
        item.children = [];
        generateVueRouters(item.children, permissions, router.children);
      }
    }
  }
}

// order docs https://sequelize.org/docs/v6/core-concepts/model-querying-basics/
class UserController extends BaseController {
  constructor() {
    super();
    this.getInfo = this.getInfo.bind(this);
    this.unlock = this.unlock.bind(this);
    this.resetPassword = this.resetPassword.bind(this);
    this.getUserHotelPermissions = this.getUserHotelPermissions.bind(this);
    this.grantHotelToUser = this.grantHotelToUser.bind(this);
    this.revokeHotelToUser = this.revokeHotelToUser.bind(this);
    this.getGrantedHotels = this.getGrantedHotels.bind(this);
    this.grantHotelPermissionsToUser = this.grantHotelPermissionsToUser.bind(this);
  }

  getModel() {
    return UserModel;
  }

  /**
   * 解析查询条件
   * @returns {Object}
   */
  getFindWhere(req) {
    const { xfull_name, login_name } = req.query;
    const where = {};

    if (trim(xfull_name)) {
      where.xfull_name = { [Op.like]: `%${trim(xfull_name)}%` };
    }

    if (trim(login_name)) {
      where.login_name = { [Op.like]: `%${trim(login_name)}%` };
    }

    return { where: isEmpty(where) ? undefined : where };
  }

  getInclude(req) {
    const { hotel_id } = req.user;
    if (hotel_id) {
      return {
        include: [{
          model: UserHotels,
          as: 'user_hotel',
          required: true,
          where: {
            hotel_id
          }
        }]
      };
    }

    return {};
  }

  async create(req, res, next) {
    try {
      const result = validationResult(req);
      if (!result.isEmpty()) {
        throw new ExpressValidationError({ errors: result.array() });
      }

      const { hotel_id } = req.params;

      delete req.body.id;
      delete req.body.version;
      delete req.body.created_at;
      delete req.body.updated_at;

      req.body.created_id = req.user.id;
      req.body.updated_id = req.user.id;
      req.body.password = req.body.password_new;

      const transaction = await getSequelize().transaction();
      req.transaction = transaction;
      const data = await UserModel.create(req.body, { req, transaction });
      if (hotel_id) {
        await UserHotels.create({
          user_id: data.id,
          hotel_id,
          is_admin: false,
          created_id: req.user.id
        }, { transaction, req });
      }
      await transaction.commit();
      res.sendOk(data);
    }
    catch (e) {
      next(e);
    }
  }

  async findUserByLoginName(req, res, next) {
    const user = await UserModel.findOne({
      where: {
        login_name: req.params.login_name
      }
    });

    res.sendOk(user);
  }

  async login(req, res, next) {
    try {
      let {
        username, password /* 视图 */
      } = req.body;

      // view_role admin / hotel
      const view_role = req.headers['view-role'];

      const sequelize = getSequelize();
      const login_name = trim(username);
      const transaction = await sequelize.transaction();
      req.transaction = transaction;

      assert(login_name, 'login name is required');
      assert(password, 'password is required');

      let user = await UserModel.findOne({
        where: { login_name },
        transaction,
        rejectOnEmpty: new Error('无效的用户名或密码')
      });

      let error;
      const pwd = user.getPassword(password, user.salt);
      console.log(pwd);
      if (pwd !== user.password) {
        error = new Error('无效的用户名或密码');
      }

      if (user.status !== 'Active') {
        throw new Error(`用户状态为${user.status}, 无法登录`);
      }

      if (user.lock_date) {
        throw new Error(`user account was locked at ${dayjs(user.lock_date).format('YYYY-MM-DD HH:mm:ss')}`);
      }

      if (error) {
        user.error_times += 1;
        if (user.error_times >= 5) {
          user.status = 'Locked';
          user.lock_date = new Date();
        }

        await user.save({ transaction, req });
        await transaction.commit();
        res.send({
          errCode: 1,
          errMsg: error.message
        });
      }
      else {
        user.last_login = new Date();
        user.error_times = 0;

        const result = {
          login_name: user.login_name,
          user_type: user.user_type
        };
        const options = {
          id: user.id,
          login_name: user.login_name
        };

        if (view_role === 'hotel') {
          const hotels = await user.getGrantedHotels();
          result.hotels = hotels;
          if (user.last_hotel_id && find(hotels, { id: user.last_hotel_id })) {
            options.hotel_id = user.last_hotel_id;
          }
          else if (hotels.length > 0) {
            user.last_hotel_id = hotels[0].id;
            options.hotel_id = user.last_hotel_id;
          }
        }

        await user.save({ transaction, req });
        await transaction.commit();

        console.log('optioins', options);
        result.token = signToken(options);
        res.sendOk(result);
      }
    }
    catch (e) {
      next(e);
    }
  }

  async unlock(req, res, next) {
    try {
      const { user_id } = req.body; // 需要被解锁的用户id
      // const { user_id, password: grant_password } = req.body; // 需要被解锁的用户id
      // const { id: grant_user_id } = req.user;
      // const grant_user = await UserModel
      // .findOne({ where: { id: grant_user_id }, rejectOnEmpty: new Error('授权用户不存在') });
      // if (!grant_user.checkPassword(grant_password)) {
      //   throw new Error('无效的授权密码');
      // }
      const user = await UserModel.findOne({ where: { id: user_id }, rejectOnEmpty: new Error('用户不存在') });
      if (user.status === 'Locked' && user.lock_date) {
        user.status = 'Active';
        user.lock_date = null;
        await user.save({ validate: false });
      }
      else {
        throw new Error('无法解锁');
      }

      res.sendOk();
    }
    catch (e) {
      next(e);
    }
  }

  async resetPassword(req, res, next) {
    try {
      const { user_id, password, grant_password } = req.body; // 需要被解锁的用户id
      const { id: grant_user_id } = req.user;
      const grant_user = await UserModel
        .findOne({ where: { id: grant_user_id }, rejectOnEmpty: new Error('授权用户不存在') });
      if (!grant_user.checkPassword(grant_password)) {
        throw new Error('无效的授权密码');
      }
      const user = await UserModel.findOne({ where: { id: user_id }, rejectOnEmpty: new Error('用户不存在') });

      user.password = user.getPassword(password, user.salt);
      user.password_changed = new Date();
      await user.save({ validate: false });

      res.sendOk();
    }
    catch (e) {
      next(e);
    }
  }

  async getInfo(req, res, next) {
    const view_role = req.headers['view-role']; // admin / hotel

    let { id: user_id, hotel_id } = req.user;
    const user = await UserModel.findOne({ where: { id: user_id }, rejectOnEmpty: new Error('用户不存在') });
    const tree = [];
    const vueRouters = [];
    const permissions = [];

    const hotels = await user.getGrantedHotels();
    let app_parameters = [];

    if (view_role === 'hotel') {
      if (hotel_id) {
        if (user.user_type === 'Administrator') {
          await SysMenus.getMenuRouters(tree);
        }
        else {
          await SysMenus.getUserHotelMenuRouters(tree, user_id, hotel_id);
        }
      }
      else { // get default hotel_id
        const sequelize = getSequelize();
        const transaction = await sequelize.transaction();
        req.transaction = transaction;

        if (user.last_hotel_id && find(hotels, { id: user.last_hotel_id })) {
          hotel_id = user.last_hotel_id;
        }
        else if (hotels.length > 0) {
          user.last_hotel_id = hotels[0].id;
          hotel_id = user.last_hotel_id;
        }

        if (hotel_id) {
          if (user.user_type === 'Administrator') {
            await SysMenus.getMenuRouters(tree);
          }
          else {
            await Hotels.checkLicense(hotel_id);
            await SysMenus.getUserHotelMenuRouters(tree, user_id, hotel_id);
          }
          user.last_hotel_id = hotel_id;
          user.last_login = new Date();
          await user.save({ transaction, req });
          await transaction.commit();
        }
      }

      if (hotel_id) {
        app_parameters = await AppParameterValues.findAll({
          where: { hotel_id }
        });
      }
    }
    else if (view_role === 'admin') {
      hotel_id = undefined;
    }

    generateVueRouters(vueRouters, permissions, tree);
    if (user.user_type === 'Administrator') {
      permissions.length = 0;
      permissions.push('*.*.*');
    }

    res.sendOk({
      login_name: user.login_name,
      user_type: user.user_type,
      user_id: user.id,
      hotel_id,
      routers: vueRouters,
      permissions,
      hotels,
      app_parameters,
      token: signToken({
        ...(hotel_id ? { hotel_id } : {}),
        id: user.id,
        login_name: user.login_name
      })
    });
  }

  async getUserHotelPermissions(req, res, next) {
    const { user_id, hotel_id } = req.params;

    try {
      const routers = await SysMenus.findAll({
        where: { disabled: false },
        // include: [{
        //   model: UserPermissions,
        //   as: 'user_permission',
        //   attributes: ['hotel_id', 'user_id'],
        // [sequelize.literal('cast( user_id IS NOT NULL AS UNSIGNED )'), 'granted']
        //   required: false,
        //   where: {
        //     hotel_id,
        //     user_id
        //   }
        // }],
        order: [['sequence', 'ASC'], ['code', 'ASC']]
      });
      const tree = [];

      generatePermissionTree(tree, routers.map((e) => e.toJSON()), 0);

      const grantedKeys = await UserPermissions.findAll({
        where: {
          user_id,
          hotel_id
        },
        attributes: ['permission_id']
      });

      res.sendOk({
        tree,
        granted: map(grantedKeys, 'permission_id')
      });
    }
    catch (e) {
      next(e);
    }
  }

  // 授权主体
  async grantHotelToUser(req, res, next) {
    try {
      const sequelize = getSequelize();
      const transaction = await sequelize.transaction();
      req.transaction = transaction;
      req.body.created_id = req.user.id;
      const data = await UserHotels.create(req.body, { transaction, req });
      await transaction.commit();

      res.sendOk(data);
    }
    catch (e) {
      if (e.name === 'SequelizeUniqueConstraintError') {
        next(new Error('用户已授权当前主体，请重复操作'));
      }
      else {
        next(e);
      }
    }
  }

  // 撤销授权主体
  async revokeHotelToUser(req, res, next) {
    try {
      const { user_id, hotel_id } = req.body;
      const sequelize = getSequelize();
      const transaction = await sequelize.transaction();
      req.transaction = transaction;
      const data = await UserHotels.findOne({ where: { user_id, hotel_id }, transaction, rejectOnEmpty: new Error('数据不存在') });
      const result = await data.destroy({ transaction, req });
      await transaction.commit();

      res.sendOk(result);
    }
    catch (e) {
      next(e);
    }
  }

  async getGrantedHotels(req, res, next) {
    try {
      const { id } = req.params;
      const user = await UserModel.findOne({ where: { id }, rejectOnEmpty: new Error('用户不存在') });
      const data = await user.getGrantedHotels();

      res.sendOk({
        rows: data
      });
    }
    catch (e) {
      next(e);
    }
  }

  // 授予权限
  async grantHotelPermissionsToUser(req, res, next) {
    try {
      const { user_id, hotel_id } = req.params;
      const sequelize = getSequelize();
      const transaction = await sequelize.transaction();
      req.transaction = transaction;
      await UserPermissions.destroy({
        where: {
          user_id,
          hotel_id
        },
        transaction,
        individualHooks: true
      });
      const data = await UserPermissions.bulkCreate(req.body.map((permission_id) => ({
        user_id,
        hotel_id,
        permission_id,
        created_id: req.user.id
      })), { transaction });
      await transaction.commit();

      res.sendOk(data);
    }
    catch (e) {
      next(e);
    }
  }

  // 切换登录主体，更新token
  async updateLoginHotel(req, res, next) {
    try {
      const { hotel_id } = req.params;
      const { id: user_id } = req.user;
      const sequelize = getSequelize();
      const transaction = await sequelize.transaction();
      req.transaction = transaction;

      const user = await UserModel.findOne({
        where: { id: user_id },
        rejectOnEmpty: new Error('无效的用户名'),
        transaction
      });

      if (user.user_type !== 'Administrator') {
        await UserHotels.findOne({
          where: { user_id, hotel_id },
          rejectOnEmpty: new Error('无当前主体权限'),
          transaction
        });
      }

      await Hotels.checkLicense(hotel_id);

      user.last_hotel_id = hotel_id;
      user.last_login = new Date();
      await user.save({ transaction, req });
      await transaction.commit();

      res.sendOk({
        token: signToken({ hotel_id, id: user.id, login_name: user.login_name })
      });
    }
    catch (e) {
      next(e);
    }
  }

  // update password
  async updatePassword(req, res, next) {
    try {
      const { id: user_id } = req.user;
      const { old_password, password, confirm_password } = req.body;
      if (!password) {
        throw new Error('请输入新密码');
      }
      if (password !== confirm_password) {
        throw new Error('两次输入的密码不一致');
      }

      if (password.length < 6) {
        throw new Error('密码长度不能小于6位');
      }
      const sequelize = getSequelize();
      const transaction = await sequelize.transaction();
      req.transaction = transaction;

      const user = await UserModel.findOne({
        where: { id: user_id },
        rejectOnEmpty: new Error('用户不存在'),
        transaction
      });

      if (!user.checkPassword(old_password)) {
        throw new Error('原密码错误');
      }

      user.password = user.getPassword(password, user.salt);
      user.password_changed = new Date();

      await user.save({ transaction, req });
      await transaction.commit();

      res.sendOk(user);
    }
    catch (e) {
      next(e);
    }
  }
}

export default new UserController();
